﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace httpd
{
    

    class Program
    {
        public static readonly string[,] ContentTypeMapping = new string[,]{
            { ".txt", "text/plain" },
            { ".html", "text/html" },
            { ".htm", "text/html" },
            { ".xml", "application/xml" },
            { ".xhtml", "application/xhtml+xml" },
            { ".json", "application/json" },
            { ".css", "text/css" },
            { ".js", "application/javascript" },
            { ".jpg", "image/jpeg" },
            { ".jpeg", "image/jpeg" },
            { ".png", "image/png" },
            { ".gif", "image/gif" },
            { ".bmp", "image/bmp" },
            { ".svg", "image/svg+xml" },
            { ".ico", "image/x-icon" },
            { ".pdf", "application/pdf" },
            { ".doc", "application/msword" },
            { ".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document" },
            { ".xls", "application/vnd.ms-excel" },
            { ".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" },
            { ".ppt", "application/vnd.ms-powerpoint" },
            { ".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation" },
            { ".mp3", "audio/mpeg" },
            { ".mp4", "video/mp4" },
            { ".avi", "video/x-msvideo" },
            { ".mov", "video/quicktime" },
            { ".zip", "application/zip" },
            { ".rar", "application/vnd.rar" },
            { ".tar", "application/x-tar" },
            { ".gz", "application/gzip" }
        };

        const string plainText = @"HTTP/1.1 200 OK
Content-Length: {0}
Content-Type: {1}
Date: {2:R}
Server: ZB http Server

";


        static string workPath=Directory.GetCurrentDirectory();

        static string getFileContentType(string fileName)
        {
            string ext = Path.GetExtension(fileName);
            for (int i = 0; i < ContentTypeMapping.GetLength(0); i++)
            {
                if (ext == ContentTypeMapping[i, 0])
                    return ContentTypeMapping[i, 1];
            }
            return "application/octet-stream";
        }

        static void sendContent(Socket socket, String contentType, String text)
        {
            DateTime now = DateTime.Now;
            byte[] body = Encoding.UTF8.GetBytes(text);
            byte[] header = Encoding.UTF8.GetBytes(String.Format(plainText, body.Length, contentType,  now));
            socket.Send(header);
            socket.Send(body);
        }

        static void onRequest(Socket socket, String requestText)
        {
            
            int idx = requestText.IndexOf("\r\n");
            string line = requestText.Substring(0, idx);
            System.Console.WriteLine(line);

            idx = line.IndexOf(' ') + 1;
            int end = line.IndexOf(' ', idx);
            string rel_url = line.Substring(idx, end - idx).Trim();
            string url=Path.GetFullPath(workPath+rel_url);
            System.Console.WriteLine("rel_url : "+rel_url);                                                                                                                                                                                                         
            if(Directory.Exists(url)) //目录
            {
                if(!rel_url.EndsWith("/")) rel_url+="/";
                StringBuilder sb = new StringBuilder("<html><body>");
                var parentDir=Directory.GetParent(url);
                if(parentDir!=null)
                {
                    sb.Append("<a href=\"").Append(rel_url).Append("..").Append("\">");
                    sb.Append("/..").AppendLine("</a><br/>");
                }
                foreach(var dir in Directory.EnumerateDirectories(url))
                {
                    String shortName=Path.GetFileName(dir);
                    sb.Append("<a href=\"").Append(rel_url).Append(shortName).Append("\">");
                    sb.Append('/').Append(shortName).AppendLine("</a><br/>");
                }
                foreach (var file in Directory.EnumerateFiles(url))
                {
                    String shortName=Path.GetFileName(file);
                    sb.Append("<a href=\"").Append(rel_url).Append(shortName).Append("\">");
                    sb.Append(shortName).AppendLine("</a><br/>");
                }
                sb.Append("</body></html>");
                sendContent(socket, "text/html", sb.ToString());
            }
            else if (File.Exists(url)) //文件
            {
                String contextType=getFileContentType(url);
                sendContent(socket,contextType,File.ReadAllText(url));
            }
            else 
                sendContent(socket,"text/plain","not found!");
        }

        static void Main(string[] args)
        {
            int port = 80;
            if (args.Length > 0)
                int.TryParse(args[0], out port);
            if (args.Length > 1)
                workPath = Path.GetFullPath(args[1]);
            Socket socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            socketWatch.Bind(new IPEndPoint(IPAddress.Parse("127.0.0.1"), port));
            socketWatch.Listen(20); // 参数表示最多可容纳的等待接受的传入连接数，不包含已经建立连接的。

            System.Console.WriteLine("服务启动：localhost:" + port);
            Thread thread = new Thread(delegate (object obj)
            {
                Socket socketListen = (Socket)obj;
                while (true)
                {
                    using (Socket socket = socketListen.Accept())
                    {
                        byte[] data = new byte[1024 * 4]; // 浏览器发来的数据
                        int length = socket.Receive(data, 0, data.Length, SocketFlags.None);
                        if (length > 0)
                        {
                            string requestText = Encoding.UTF8.GetString(data, 0, length);
                            onRequest(socket, requestText);
                        }
                        socket.Shutdown(SocketShutdown.Both);
                        socket.Close();
                    }
                }
            });

            thread.IsBackground = true;
            thread.Start(socketWatch);
            System.Console.Read();
        }
    }
}
